#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>

#include <rtdk.h>
#include <native/timer.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <native/task.h> 
#include "msgProtocol.h"

#define ELEVATOR_COUNT 5

int sockfd[ELEVATOR_COUNT], portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
char buffer[1000];
RTIME sleepTime = 100000000LL;

//simulate 5 elevators with 5 different tasks
RT_TASK tasks[ELEVATOR_COUNT];

void error(const char *msg){
    perror(msg);
    exit(0);
}

void sendMessage(char *msg){
    
	int i;
	for(i = 0; i < ELEVATOR_COUNT; i++){
		bzero(buffer,1000);
    	sprintf(buffer, msg);
		n = write(sockfd[i],buffer,strlen(buffer));
    	if (n < 0) 
        	error("ERROR writing to socket");
	}
    
    
}

//for testRegisterElevator test
void sendInitializeInfo(void *arg){
	int i = *(int *)arg;
    char initMsg[1000];
    init(initMsg,i);
    sendMessage(initMsg);
	//printf("%d sent msg to dis\n", i);	
}

void receiveAcknowledgement(void *arg) {
	int i = *(int *)arg;
	//receive acknowlegment from dispatcher
	bzero(buffer,256);
	n = read(sockfd[i-1],buffer,255);
	if (n < 0) { error("ERROR reading from socket"); }
	printf("%s\n",buffer);
    bzero(buffer,256);
}

//for testRequestUpdate test
void receiveRequestUpdate(void *arg) {
	int i = *(int *)arg;
	
	bzero(buffer,1000);
	n = read(sockfd[i-1],buffer,999);
	if (n < 0) {error("ERROR reading from socket"); }
	printf("%d", i);
	printf("%s\n",buffer);
	bzero(buffer,1000);
	
}

//for testUpdate test
void sendUpdate(void *arg){
	int i = *(int *)arg;
    char updateMsg[1000];
    update(updateMsg,i, i, i, i, i);
    sendMessage(updateMsg);
}

//for testUpdate test
void sendHallCallReceived(void *arg){
	int i = *(int *)arg;
    char Msg[1000] = "Elevator Received Hall Call\n";
    //update(updateMsg,i, i, i, i, i);
    sendMessage(Msg);
}

//for testReceiveUpdateRequest test
void sendResponse(void *arg){
	printf("Sending\n");
	char *msg = "Sending Update\n";
	sendMessage(msg);
}

//for receiveHallCall test
void receiveHallCall(){
	int i;
	for(i = 0; i < ELEVATOR_COUNT; i++){
		bzero(buffer,1000);
		n = read(sockfd[i],buffer,999);
		if (n < 0) { error("ERROR reading from socket"); }
		printf("%s\n",buffer);
		bzero(buffer,1000);
	}
}

void joinTasks(RT_TASK tasks[]) {
	int i;
	for(i = 0;i < 5; i++){
		rt_task_join(&tasks[i]);
	}
}

void deleteTasks(RT_TASK tasks[]) {
	int i;
	for(i = 0; i < 5; i++){
		rt_task_delete(&tasks[i]);
	}
}

void catch_signal(int sig){}

int main(int argc, char *argv[])
{
	signal(SIGTERM, catch_signal);
	signal(SIGINT, catch_signal);
	setbuf(stdout, NULL);

	mlockall(MCL_CURRENT|MCL_FUTURE);

    
    //char *values;
    
	//need 3 arguements - elevator filename, server hostname, server port
    if (argc < 3) {
       fprintf(stderr,"usage %s hostname port\n", argv[0]);
       exit(0);
    }

	//portno holds server port
    portno = atoi(argv[2]);

	//server holds server socket info
	server = gethostbyname(argv[1]);
	if (server == NULL) {
    	fprintf(stderr,"ERROR, no such host\n");
    	exit(0);
	}

	//Creates an array of sockets for elevators running on different ports
	int j;
	for(j = 0;j < 5; j++){
		sockfd[j] = socket(AF_INET, SOCK_STREAM, 0);
    	if (sockfd[j] < 0) 
        	error("ERROR opening socket");
	}
		
    
    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr, 
         (char *)&serv_addr.sin_addr.s_addr,
         server->h_length);
    serv_addr.sin_port = htons(portno);
	
	//Connects the array of sockets for the elevators to the server
	for(j = 0; j < 5; j++){
		if (connect(sockfd[j],(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) 
        	error("ERROR connecting");
	}
	
	for(j = 0; j < 5; j++){
		rt_task_create(&tasks[j], NULL, 0, 99, T_JOINABLE);
		int num = j+1;
		rt_task_start(&tasks[j], &sendInitializeInfo, &num);
	}

	joinTasks(tasks);	
	deleteTasks(tasks);
	
/*
	RT_TASK receive;sendUpdate
	rt_task_create(&receive, NULL, 0, 99, T_JOINABLE);
	rt_task_start(&receive, &receiveAcknowledgement, NULL);
	rt_task_join(&receive);
	rt_task_delete(&receive);
*/
	/*
	for(j = 0; j < 5; j++){
		rt_task_create(&tasks2[j], NULL, 0, 99, T_JOINABLE);
		int num = j+1;
		rt_task_start(&tasks2[j], &receiveAcknowledgement, &num);
		
		
	}

	joinTasks(tasks2);
	deleteTasks(tasks2);
	

	for(j = 0; j < 5; j++){
		rt_task_create(&tasks[j], NULL, 0 , 99, T_JOINABLE);
		int num = j+1;
		rt_task_start(&tasks[j], &receiveRequestUpdate, &num);
		
	}
	
	printf("Done Receiving\n");
	joinTasks(tasks);
	deleteTasks(tasks);
	printf("send update\n");
	
	for(j = 0; j < 5; j++){
		rt_task_create(&tasks[j], NULL, 0, 99, T_JOINABLE);
		rt_task_start(&tasks[j], &sendResponse, NULL);
		printf("sending\n");
	}*/
	
	RT_TASK tasks2[ELEVATOR_COUNT];

	for(j = 0; j < 5; j++){
		rt_task_create(&tasks2[j], NULL, 0, 99, T_JOINABLE);
		int num = j+1;
		rt_task_start(&tasks2[j], &sendUpdate, &num);
	}

	joinTasks(tasks2);
	deleteTasks(tasks2);
	
	for(j = 0; j < 5; j++){
		rt_task_create(&tasks2[j], NULL, 0, 99, T_JOINABLE);
		int num = j+1;
		rt_task_start(&tasks2[j], &sendHallCallReceived, &num);
	}
	
	joinTasks(tasks2);
	deleteTasks(tasks2);

	for(j = 0; j < 5; j++){
		rt_task_create(&tasks2[j], NULL, 0, 99, T_JOINABLE);
		int num = j+1;
		rt_task_start(&tasks2[j], &sendHallCallReceived, &num);
	}

	joinTasks(tasks2);
	deleteTasks(tasks2);

	for(j = 0; j < 5; j++){
		rt_task_create(&tasks2[j], NULL, 0, 99, T_JOINABLE);
		int num = j+1;
		rt_task_start(&tasks2[j], &sendUpdate, &num);
	}

	joinTasks(tasks2);
	deleteTasks(tasks2);

	for(j = 0; j < 5; j++){
		close(sockfd[j]);
	}

    return 0;
}
